home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
SGI Developer Toolbox 6.1
/
SGI Developer Toolbox 6.1 - Disc 4.iso
/
public
/
SciAn
/
src
/
ScianDatabase.c
< prev
next >
Wrap
C/C++ Source or Header
|
1994-08-01
|
27KB
|
1,091 lines
/*
Tzong-Yow hwu
need changes to make:
1. keep the number of deleted nodes of the NameTree for reconstruction
the NameTree
*/
#include "Scian.h"
#include "ScianTypes.h"
#include "ScianIDs.h"
#include "ScianLists.h"
#include "ScianSymbols.h"
#include "ScianGarbageMan.h"
#include "ScianDatabase.h"
#define DB_MAXKEYS 30 /* maximum number of keys allowed in the search key list */
struct LListNode
{
struct LListNode *next;
Bool isVolatile; /* if it's a volatile object */
ObjPtr object;
};
struct NameTreeNode
{
struct NameTreeNode *left;
struct NameTreeNode *right;
struct LListNode *list;
char *name;
int linkLength;
/* the maximum number of link length of its left and right child */
Bool deleteFlag;
};
typedef struct LListNode llistNode;
typedef struct NameTreeNode nameTreeNode;
typedef enum {FAILED = 0, SUCCESSANDNOMORE = 1, SUCCESSANDMORE = 2} Triad;
/* a dummy object to be associated with the database system */
static ObjPtr gcObject;
static nameTreeNode *NameTree;
static llistNode *ClassList[N_CLASS_IDS + 1];
/* class id starting from 1
Use 0th array element for objects whose class var is NULLOBJ */
#ifdef PROTO
static Triad addToNameTree(nameTreeNode **root, llistNode *listNode, char *name);
static Bool deleteFromNameTree(ObjPtr object, char *name);
static ObjPtr searchNameTree(char *name, int nKeys, NameTyp *keyId, ObjPtr *keyObj);
static nameTreeNode *createNameTreeNode(llistNode *node, char *objname);
static void freeNameTreeNode(nameTreeNode *node);
static Bool addToClassArray(llistNode *node, int classid);
static Bool deleteFromClassArray(ObjPtr object, int classid);
static ObjPtr searchClassArray(int classid, int nKeys, NameTyp *keyId, ObjPtr *keyObj);
static ObjPtr dumbSearch(int nKeys, NameTyp *keyId, ObjPtr *keyObj);
static llistNode *createLListNode(ObjPtr object);
static void freeLListNode(llistNode *node);
static void addToLList(llistNode **list, llistNode *node);
static Bool deleteFromLList(llistNode **list, ObjPtr object);
static ObjPtr searchLList(llistNode *list, int nKeys, NameTyp *keyId, ObjPtr *keyObj);
static Bool matched(ObjPtr object, int nKeys, NameTyp *keyId, ObjPtr *keyObj);
#else
static Triad addToNameTree();
static Bool deleteFromNameTree();
static ObjPtr searchNameTree();
static nameTreeNode *createNameTreeNode();
static void freeNameTreeNode();
static Bool addToClassArray();
static Bool deleteFromClassArray();
static ObjPtr searchClassArray();
static ObjPtr dumbSearch();
static llistNode *createLListNode();
static void freeLListNode();
static void addToLList();
static Bool deleteFromLList();
static ObjPtr searchLList();
static Bool matched();
#endif
/**************************** DATABASE OPERATIONS ***************************/
#ifdef PROTO
Bool AddToDatabase(ObjPtr object, Bool isVolatile)
#else
Bool AddToDatabase(object, isVolatile)
ObjPtr object;
Bool isVolatile;
#endif
{
ObjPtr var;
char *name;
char *empty = "";
int noclass = 0;
int classid;
llistNode *nameTreeListNode, *classArrayListNode;
if (!object)
return false;
/* figure out the name of the object */
MakeVar(object, NAME);
var = GetVar(object, NAME);
if (!var)
{
/* var is a NULLOBJ */
name = empty;
}
else
{
name = GetString(var);
}
/* figure out the classid of the object */
MakeVar(object, CLASSID);
var = GetVar(object, CLASSID);
if (!var)
{
classid = noclass;
}
else
{
classid = GetInt(var);
}
/* make a ClassArrayListNode and a NameTreeListNode and insert them
into the NameTree and ClassArray respectively */
if (!(nameTreeListNode = createLListNode(object)))
{
return false;
}
if (!(classArrayListNode = createLListNode(object)))
{
freeLListNode(nameTreeListNode);
return false;
}
/* set up volatile flag */
nameTreeListNode->isVolatile = classArrayListNode->isVolatile = isVolatile;
if (FAILED == addToNameTree(&NameTree, nameTreeListNode, name) ||
false == addToClassArray(classArrayListNode, classid))
{
freeLListNode(nameTreeListNode);
freeLListNode(classArrayListNode);
return false;
}
return true;
} /* AddToDatabase */
#ifdef PROTO
Bool DeleteObjFromDatabase(ObjPtr object)
#else
Bool DeleteObjFromDatabase(object)
ObjPtr object;
#endif
/* The deleteFlag of a Nametreenode is turned on if it has an empty
list, i.e., the list ptr points to null */
{
char *name;
char *empty = "";
ObjPtr var;
int classid;
int noclass = 0;
llistNode *treeListNode, classArrayListNode;
if (!object)
return false;
MakeVar(object, NAME);
var = GetVar(object, NAME);
if (!var)
{
/* var is a NULLOBJ */
name = empty;
}
else
{
name = GetString(var);
}
MakeVar(object, CLASSID);
var = GetVar(object, CLASSID);
if (!var)
{
classid = noclass;
}
else
{
classid = GetInt(var);
}
/* if the object is not in the name tree, it should not be in the
class array either */
if (!deleteFromNameTree(object, name))
{
if (deleteFromClassArray(object, classid))
{
ReportError("DeleteObjFromDatabase", "database is in inconsistent state");
}
/*
Is ther a need to report an error if the object is not in the database
else
{
ReportError("DeleteObjFromDatabase", "object not found");
}
*/
return false;
}
if (!deleteFromClassArray(object, classid))
{
ReportError("DeleteObjFromDatabase", "database is in inconsistent state");
return false;
}
return true;
} /* DeleteObjFromDatabase */
#ifdef PROTO
ObjPtr SearchDatabase(ObjPtr keylist)
#else
ObjPtr SearchDatabase(keylist)
ObjPtr keylist;
#endif
/* if there is Name primarily search by NAME,
else if there is CLASSID primarily search by CLASSID,
else performs a linear search on the class array by the keys present */
/* returns NULLOBJ if error, an emptylist if not found, a list otherwise */
{
char *emptyname = "";
Bool isName = false; /* is name there as a key in the list */
char *name;
ObjPtr classObj;
Bool isClassid = false; /* is class id there as a key in the list */
int classid;
int noclass = 0;
/* construct an array of name types corresponding to keylist */
NameTyp keyId[DB_MAXKEYS];
/* construct an array of objptr from keylist */
ObjPtr keyObj[DB_MAXKEYS];
int index;
int keyIndex;
int nKeys;
int nElements;
if (!keylist)
{
/* keylist is a NULLOBJ */
return dumbSearch(0, (NameTyp *) 0, (ObjPtr *) 0);
}
else if (!IsList(keylist) || ((nElements=ListCount(keylist)) % 2))
{
/* keylist must be a nonempty list and has even number of elements */
ReportError("SearchDatabase", "Improper key list");
return NULLOBJ;
}
else if (nElements == 0)
{
/* empty key list, return the whole database */
return dumbSearch(0, (NameTyp *) 0, (ObjPtr *) 0);
}
else if ((nKeys = nElements / 2) > DB_MAXKEYS)
{
ReportError("SearchDatabase", "Exceeds the maximum number of allowed keys");
return NULLOBJ;
}
keyIndex = 0;
for (index = 0; index < nKeys; index++)
{
ObjPtr symbol;
ObjPtr obj;
NameTyp id;
symbol = GetListElem(keylist, 2 * index);
obj = GetListElem(keylist, 2 * index + 1);
if (!IsSymbol(symbol))
{
ReportError("SearchDatabase", "Improper key list");
return NULLOBJ;
}
id = GetSymbolID(symbol);
if (id == NAME)
{
if (isName == true)
{
ReportError("SearchDatabase", "Double NAME keys in keylist");
return NULLOBJ;
}
if (!obj)
{
name = emptyname;
}
else if (!IsString(obj))
{
ReportError("SearchDatabase", "non-string obj after NAME symbol");
return NULLOBJ;
}
else
{
name = GetString(obj);
}
if (!name)
name = emptyname;
isName = true;
}
else if (id == CLASSID)
{
if (isClassid == true)
{
ReportError("SearchDatabase", "Double CLASSID keys in keylist");
return NULLOBJ;
}
if (!obj)
{
classid = noclass;
}
else if (!IsInt(obj))
{
ReportError("SearchDatabase", "non-class obj after CLASS symbol");
return NULLOBJ;
}
else
{
classid = GetInt(obj);
}
if (classid > N_CLASS_IDS || classid < 0)
{
ReportError("SearchDatabase", "Class id exceeds range");
return NULLOBJ;
}
classObj = obj;
isClassid = true;
}
else
{
keyId[keyIndex] = id;
keyObj[keyIndex] = obj;
keyIndex++;
}
}
/* the search routines always return a list obj or NULLOBJ if failed */
if (isName)
{
if (isClassid)
{
keyId[keyIndex] = CLASSID;
keyObj[keyIndex] = classObj;
}
/* NAME key is not included */
return searchNameTree(name, nKeys - 1, keyId, keyObj);
}
else if (isClassid)
{
/* CLASSID key is not included */
return searchClassArray(classid, nKeys - 1, keyId, keyObj);
}
else
{
return dumbSearch(nKeys, keyId, keyObj);
}
}
/* method use only old way of c function definition */
ObjPtr MarkDatabase(gcObject)
ObjPtr gcObject;
{
/* it's sufficient to mark all the objects in the Class Array */
/* easier to traverse */
int i;
llistNode *runner;
for (i = 0; i <= N_CLASS_IDS; i++)
for (runner = ClassList[i]; runner; runner = runner->next)
{
if (runner->isVolatile == DB_NONVOLATILE)
{
MarkObject(runner->object);
}
}
return ObjTrue;
} /* MarkDatabase */
#ifdef PROTO
void InitDatabase(void)
#else
void InitDatabase()
#endif
{
int i;
gcObject = NewObject(NULLOBJ, 0);
AddToReferenceList(gcObject);
SetMethod(gcObject, MARK, MarkDatabase);
/* empty database, do an initialization first */
NameTree = (nameTreeNode *) 0;
for (i = 0; i <= N_CLASS_IDS; i++)
{
ClassList[i] = (llistNode *) 0;
}
return;
} /* InitDatabase */
#ifdef PROTO
void KillDatabase(void)
#else
void KillDatabase()
#endif
{
RemoveFromReferenceList(gcObject);
return;
} /* KillDatabase */
/**************************** NameTree Operations ****************************/
/*
This is a recursive algorithm for adding a node into a binary tree and
maintain its balance. Balance in this case means the difference between
any node's left and right link length is not greater than 1.
When adding a linked-list node into the binary NameTree, three possible
results may occur:
0. Error iff an name tree node is not able be allocated or name is a
null pointer.
1. Success and the list node is inserted into the linked list of an
already existing tree node.
2. Success and a new tree node is created to accomdate this list node.
For case 2, a new node is created and the balance of the binary tree
needed to be maintained. The flow of op is like this:
When the length of the current node changed, we let the parent
to determine whether it needs to balance its subtree by returning
SUCCESSANDMORE value. Upon receiving the return value of
SUCCESSANDMORE, the current node look into its rightlength and
leftlength for determining successive actions. ? possible actions:
1. When the length of left or right child changed and their difference
is greater than 1, we need to balance the subtree rooted at the
current node. After a balancing op, the length of the current node
may be different. Return accordingly.
2. When their difference is not greater than 1, the current node needs
to determine whether it needs to increment its length by 1.
If YES, return SUCCESSANDMORE.
If NO, return SUCCESSANDNOMORE.
*/
#ifdef PROTO
static Triad addToNameTree(nameTreeNode **root, llistNode *listNode, char *name)
#else
static Triad addToNameTree(root, listNode, name)
nameTreeNode *root;
llistNode *listNode;
char *name;
#endif
{
int direction;
Triad retVal;
if (!name)
return FAILED;
if (!*root)
{
if ( !(*root = createNameTreeNode(listNode, name)) )
return FAILED;
return SUCCESSANDMORE;
}
direction = strcmp2((*root)->name, name);
if (!direction)
{
if ((*root)->deleteFlag == true)
{
(*root)->deleteFlag = false;
}
addToLList(&(*root)->list, listNode);
return SUCCESSANDNOMORE;
}
else if (direction < 0)
{
if ( SUCCESSANDMORE ==
(retVal = addToNameTree(&((*root)->right), listNode, name)) )
{
/* The root's right link length is just incremented by 1, see
if we need to do the balancing task, or changing the link length
of the root and returns SUCCESSANDMORE */
int leftLength, rightLength;
/* determine the left and right length */
leftLength = (*root)->left ? (*root)->left->linkLength : 0;
/* there must be a right child since we just added one */
rightLength = (*root)->right->linkLength;
/* Check the length difference first and balancing accordingly */
/* the length diff may only one of three possible values 0, 1, 2 */
switch(rightLength - leftLength)
{
case 0:
/* Before adding the node, the situation is that
leftLength is 1 greater than rightLength,
the link length of the root must be leftLength + 1 */
/* balanced and no need to change link length, do nothing */
retVal = SUCCESSANDNOMORE;
break;
case 1:
/* Before adding the node, the situation is that
rightLength is the same as leftLength */
/* rightLength - leftLength must be 1 after adding
the node to the right and get its rightlength
incremented by 1. */
(*root)->linkLength += 1;
retVal = SUCCESSANDMORE;
break;
case 2:
{
/* Before adding the node, the situation is that
rightLength = leftLength + 1, the link length of the
root must be old rightLength + 1 that is the current
value of rightlength */
/* needs balancing */
nameTreeNode *temp;
int newRightLength;
temp = *root;
*root = (*root)->right;
temp->right = (*root)->left;
(*root)->left = temp;
/* after the switching, we need calculate the linklength
of the node currently pointed by temp, which is the
left child of the *root now. */
/* The result of newRightLength - leftLength is either
0 or 1. That's what the balancing for. */
newRightLength = temp->right ? temp->right->linkLength : 0;
if (newRightLength == leftLength)
{
temp->linkLength -= 1;
retVal = SUCCESSANDNOMORE;
}
else
{
(*root)->linkLength += 1;
retVal = SUCCESSANDMORE;
}
}
break;
default:
/* dummy case */
break;
}
}
return retVal;
}
else
{
if ( SUCCESSANDMORE ==
(retVal = addToNameTree(&((*root)->left), listNode, name)) )
{
int leftLength, rightLength;
rightLength = (*root)->right ? (*root)->right->linkLength : 0;
leftLength = (*root)->left->linkLength;
switch(leftLength - rightLength)
{
case 0:
retVal = SUCCESSANDNOMORE;
break;
case 1:
(*root)->linkLength += 1;
retVal = SUCCESSANDMORE;
break;
case 2:
{
nameTreeNode *temp;
int newLeftLength;
temp = *root;
*root = (*root)->left;
temp->left = (*root)->right;
(*root)->right = temp;
newLeftLength = temp->left ? temp->left->linkLength : 0;
if (newLeftLength == rightLength)
{
temp->linkLength -= 1;
retVal = SUCCESSANDNOMORE;
}
else
{
(*root)->linkLength += 1;
retVal = SUCCESSANDMORE;
}
}
break;
default:
/* dummy case */
break;
}
}
return retVal;
}
} /* addToNameTree */
/* This is an iteration algorithm for pseudo deleting a node from the
name tree. A node is pseudo deleted iff it has an empty linked list.
Pseudo deletion is done by turning on the deleteFlag of the tree node. */
#ifdef PROTO
static Bool deleteFromNameTree(ObjPtr object, char *name)
#else
static Bool deleteFromNameTree(object, name)
ObjPtr object;
char *name;
#endif
{
nameTreeNode *runner;
Bool retVal;
runner = NameTree;
while(runner)
{
int direction;
direction = strcmp2(runner->name, name);
if (!direction)
{
if (runner->deleteFlag == true)
{
/*
Is there a need to report an error when object no found.
ReportError("deleteFromNameTree", "object not found");
*/
return false;
}
retVal = deleteFromLList(&runner->list, object);
if (!runner->list)
{
/* if there is no more nodes in the list, pseudo delete the node. */
runner->deleteFlag = true;
}
return retVal;
}
else if (direction < 0)
{
runner = runner->right;
}
else
{
runner = runner->left;
}
}/* while loop */
/*
Is there a need to report an error when object no found.
ReportError("deleteFromNameTree", "object not found");
*/
return false;
} /* deleteFromNameTree */
#ifdef PROTO
static ObjPtr searchNameTree(char *name, int nKeys, NameTyp *keyId, ObjPtr *keyObj)
#else
static ObjPtr searchNameTree(name, nKeys, keyId, keyObj)
char *name;
int nKeys;
NameTyp *keyId;
ObjPtr *keyObj;
#endif
{
nameTreeNode *runner;
runner = NameTree;
while(runner)
{
int direction;
direction = strcmp2(runner->name, name);
if (!direction)
{
if (runner->deleteFlag == false)
{
return searchLList(runner->list, nKeys, keyId, keyObj);
}
else
{
return NewList();
}
}
else if (direction < 0)
{
runner = runner->right;
}
else
{
runner = runner->left;
}
}/* while loop */
return NewList();
} /* searchNameTree */
#ifdef PROTO
static nameTreeNode *createNameTreeNode(llistNode *node, char *objname)
#else
static nameTreeNode *createNameTreeNode(node, objname)
llistNode *node;
char *objname;
#endif
/* Construct a new tree node and add node at the beginning of the tree node list */
{
nameTreeNode *retVal;
if (!(retVal = (nameTreeNode *) Alloc(sizeof(nameTreeNode))))
{
ReportError("createNameTreeNode", "out of memory");
return (nameTreeNode *) 0;
}
if (!(retVal->name = (char *) Alloc((strlen(objname) + 1) * sizeof(char))))
{
Free(retVal);
ReportError("createNameTreeNode", "out of memory");
return (nameTreeNode *) 0;
}
strcpy(retVal->name, objname);
retVal->left = (nameTreeNode *) 0;
retVal->right = (nameTreeNode *) 0;
retVal->list = node;
retVal->deleteFlag = false;
retVal->linkLength = 1; /* a newly created tree node is going to be
at the leaf position which is of link length
1 */
return retVal;
} /* createNameTreeNode */
#ifdef PROTO
static void freeNameTreeNode(nameTreeNode *node)
#else
static void freeNameTreeNode(node)
nameTreeNode *node;
#endif
{
Free(node->name);
Free(node);
return;
}
/**************************** ClassArray Operations ****************************/
#ifdef PROTO
static Bool addToClassArray(llistNode *node, int classid)
#else
static Bool addToClassArray(node, classid)
llistNode *node;
int classid;
#endif
{
if (classid < 0 || classid > N_CLASS_IDS)
{
ReportError("addToClassArray", "Class id exceeds range");
return false;
}
else
{
addToLList(ClassList + classid, node);
return true;
}
} /* addToClassArray */
#ifdef PROTO
static Bool deleteFromClassArray(ObjPtr object, int classid)
#else
static Bool deleteFromClassArray(object, classid)
ObjPtr object;
int classid;
#endif
{
if (classid < 0 || classid > N_CLASS_IDS)
{
ReportError("deleteFromClassArray", "Class id exceeds range");
return false;
}
else
{
return deleteFromLList(ClassList + classid, object);
}
} /* deleteFromClassArray */
#ifdef PROTO
static ObjPtr searchClassArray(int classid, int nKeys, NameTyp *keyId, ObjPtr *keyObj)
#else
static ObjPtr searchClassArray(classid, nKeys, keyId, keyObj)
int classid;
int nKeys;
NameTyp *keyId;
ObjPtr *keyObj;
#endif
{
if (classid < 0 || classid > N_CLASS_IDS)
{
ReportError("searchClassArray", "Class id exceeds range");
return NULLOBJ;
}
else
{
return searchLList(ClassList[classid], nKeys, keyId, keyObj);
}
}
#ifdef PROTO
static ObjPtr dumbSearch(int nKeys, NameTyp *keyId, ObjPtr *keyObj)
#else
static ObjPtr dumbSearch(nKeys, keyId, keyObj)
int nKeys;
NameTyp *keyId;
ObjPtr *keyObj;
#endif
{
int runner;
ObjPtr newlist;
ObjPtr retVal = NULLOBJ;
for (runner = 0; runner <= N_CLASS_IDS; runner++)
{
newlist = searchLList(ClassList[runner], nKeys, keyId, keyObj);
if (!newlist)
{
return NULLOBJ;
}
else
{
if (ListCount(newlist) > 0)
{
if (!retVal)
{
if (!(retVal = NewList()))
{
ReportError("dumbSearch", "Unable to get a new list");
return NULLOBJ;
}
}
AppendList(retVal, newlist);
}
}
}
return retVal;
}
/****************************** LList Operations *******************************/
#ifdef PROTO
static llistNode *createLListNode(ObjPtr object)
#else
static llistNode *createLListNode(object)
ObjPtr object;
#endif
{
llistNode *retVal;
if (!(retVal = (llistNode *) Alloc(sizeof(llistNode))))
{
ReportError("createLListNode", "out of memory");
return (llistNode *) 0;
}
else
{
retVal->object = object;
retVal->next = (llistNode *) 0;
return retVal;
}
} /* createLListNode */
#ifdef PROTO
static void freeLListNode(llistNode *node)
#else
static void freeLListNode(node)
llistNode *node;
#endif
{
Free(node);
return;
} /* freeLListNode */
#ifdef PROTO
static void addToLList(llistNode **list, llistNode *node)
#else
static void addToLList(list, node)
llistNode **list;
llistNode *node;
#endif
{
node->next = *list;
*list = node;
return;
}
#ifdef PROTO
static Bool deleteFromLList(llistNode **list, ObjPtr object)
#else
static Bool deleteFromLList(list, object)
llistNode **list;
ObjPtr object;
#endif
{
llistNode *current, *previous;
current = *list;
while(current)
{
if (current->object == object)
/* object found */
{
/* at the beggining of the list */
if (current == *list)
{
*list = current->next;
}
/* somewhere after first node in the list */
else
{
previous->next = current->next;
}
freeLListNode(current);
return true;
}
else
{
previous = current;
current = current->next;
}
}
/*
Is there a need to report that the object is not found within the list.
ReportError("deleteFromLList", "object not found");
*/
return false;
}
#ifdef PROTO
static ObjPtr searchLList(llistNode *list, int nKeys, NameTyp *keyId, ObjPtr *keyObj)
#else
static ObjPtr searchLList(list, nKeys, keyId, keyObj)
llistNode *list;
int nKeys;
NameTyp *keyId;
ObjPtr *keyObj;
#endif
{
llistNode *runner;
ObjPtr retVal;
if (!(retVal = NewList()))
{
ReportError("searchLList", "Unable to get a new list");
return NULLOBJ;
}
runner = list;
while(runner)
{
if (true == matched(runner->object, nKeys, keyId, keyObj))
{
if (!PrefixList(retVal, runner->object))
{
ReportError("searchLList", "Unable to Prefix to list");
return NULLOBJ;
}
}
runner = runner->next;
}
return retVal;
}
/************************** matching operation ******************************/
#ifdef PROTO
static Bool matched(ObjPtr object, int nKeys, NameTyp *keyId, ObjPtr *keyObj)
#else
static Bool matched(object, nKeys, keyId, keyObj)
ObjPtr object;
int nKeys;
NameTyp *keyId;
ObjPtr *keyObj;
#endif
{
Bool matched = true;
int i;
for (i = 0; matched && i < nKeys; i++)
{
ObjPtr varObj;
MakeVar(object, keyId[i]);
varObj = GetVar(object, keyId[i]);
if (IsString(keyObj[i]))
{
if (!IsString(varObj))
{
return false;
}
else
{
char *keyString, *varString;
keyString = GetString(keyObj[i]);
varString = GetString(varObj);
matched = strcmp2(keyString, varString) ? false : true;
}
}
else
{
matched = Equal(varObj, keyObj[i]);
}
}
return matched;
}
#ifdef PROTO
ObjPtr NewUniqueName(char *oldName, int classID)
#else
ObjPtr NewUniqueName(oldName, classID)
char *oldName;
int classID;
#endif
/*Generates a new name based on oldName which does not conflict with existing
objects of classID in the database*/
{
char trialName[400];
char *s;
int k;
if (!DatabaseConflict(oldName, classID))
{
return NewString(oldName);
}
strcpy(trialName, oldName);
s = trialName;
while (*s) ++s;
while (s > trialName && isspace(*s)) *s-- = 0;
--s;
if (s > trialName && isdigit(*s)) --s;
{
while (s > trialName && isdigit(*s)) --s;
if (s > trialName && *s == '#')
{
--s;
while (s > trialName && isspace(*s)) --s;
if (s >= trialName)
{
++s;
*s = 0;
}
}
}
while (*s) ++s;
for (k = 2; ; ++k)
{
sprintf(s, " #%d", k);
if (!DatabaseConflict(trialName, classID))
{
return NewString(trialName);
}
}
}